#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "charset.h"

static unsigned char * codemap[CODE_MAP_SIZE];
static unsigned char binit = 0;

int init_codemap()
{
	if(binit == 1){
		return 1;
	}
	memset(codemap, 0, sizeof(unsigned char *) * CODE_MAP_SIZE);
/*
* table format. 3part.
* 1 ASCII < 256
* 2 CJK u'\u4e00' < x < u'\u9fff'
* 3 Symbols 0xFFxx and iCode >= 0x3000)&&(iCode <= 0x303F
* 2 -> CJK part. 81 entry [9f-4e], each entry is 256 size. so the offset is [(high-byte - 4E) * 256 + low-byte]
* total size is 256*(9f-4e)
*/
	unsigned char ansipage[]= {
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_, //x00
		_u_, _u_, _l_, _u_, _u_, _l_, _u_, _u_, 
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_, //x10
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_d_, _d_, _d_, _s_, _s_, _d_, _s_, _d_, //x20
		_s_, _s_, _s_, _s_, _d_, _d_, _d_, _s_,
		_m_, _m_, _m_, _m_, _m_, _m_, _m_, _m_, //x30
		_m_, _m_, _d_, _d_, _s_, _s_, _s_, _d_,
		_s_, _e_, _e_, _e_, _e_, _e_, _e_, _e_, //x40
		_e_, _e_, _e_, _e_, _e_, _e_, _e_, _e_,
		_e_, _e_, _e_, _e_, _e_, _e_, _e_, _e_, //x50
		_e_, _e_, _e_, _s_, _s_, _s_, _s_, _s_,
		_s_, _e_, _e_, _e_, _e_, _e_, _e_, _e_, //x60
		_e_, _e_, _e_, _e_, _e_, _e_, _e_, _e_,
		_e_, _e_, _e_, _e_, _e_, _e_, _e_, _e_, //x70
		_e_, _e_, _e_, _s_, _s_, _s_, _s_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_, //x80
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_, //x90
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_, //xa0
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_, //xb0
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_e_, _e_, _e_, _e_, _e_, _e_, _e_, _e_, //xc0
		_e_, _e_, _e_, _e_, _e_, _e_, _e_, _e_,
		_e_, _e_, _e_, _e_, _e_, _e_, _e_, _e_, //xd0
		_e_, _e_, _e_, _e_, _e_, _e_, _e_, _e_,
		_e_, _e_, _e_, _e_, _e_, _e_, _e_, _e_, //xe0
		_e_, _e_, _e_, _e_, _e_, _e_, _e_, _e_,
		_e_, _e_, _e_, _e_, _e_, _e_, _e_, _e_, //xf0
		_e_, _e_, _e_, _e_, _e_, _e_, _e_, _u_,
	};
	//0xFF??
	unsigned char sym1[]= {
		_u_, _d_, _d_, _s_, _d_, _s_, _s_, _d_,
		_d_, _d_, _d_, _d_, _d_, _d_, _d_, _d_,
		_m_, _m_, _m_, _m_, _m_, _m_, _m_, _m_,
		_m_, _m_, _s_, _d_, _s_, _s_, _s_, _d_,
		_s_, _f_, _f_, _f_, _f_, _f_, _f_, _f_,
		_f_, _f_, _f_, _f_, _f_, _f_, _f_, _f_,
		_f_, _f_, _f_, _f_, _f_, _f_, _f_, _f_,
		_f_, _f_, _s_, _s_, _s_, _s_, _s_, _s_,
		_d_, _f_, _f_, _f_, _f_, _f_, _f_, _f_,
		_f_, _f_, _f_, _f_, _f_, _f_, _f_, _f_,
		_f_, _f_, _f_, _f_, _f_, _f_, _f_, _f_,
		_f_, _f_, _s_, _s_, _s_, _s_, _s_, _s_,
		_s_, _s_, _s_, _s_, _s_, _s_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _s_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
	};
	// 0x30xx
	unsigned char sym2[]= {
		_d_, _d_, _d_, _d_, _s_, _s_, _s_, _m_,
		_s_, _s_, _d_, _d_, _s_, _s_, _s_, _s_,
		_s_, _s_, _s_, _s_, _s_, _s_, _s_, _s_,
		_s_, _s_, _s_, _s_, _s_, _s_, _s_, _s_,
		_s_, _s_, _s_, _s_, _s_, _s_, _s_, _s_,
		_s_, _s_, _s_, _s_, _s_, _s_, _s_, _s_,
		_s_, _s_, _s_, _s_, _s_, _s_, _s_, _s_,
		_s_, _s_, _s_, _s_, _s_, _s_, _s_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
		_u_, _u_, _u_, _u_, _u_, _u_, _u_, _u_,
	};

	unsigned char * p = (unsigned char *)malloc(sizeof(unsigned char) * CODE_MAP_SIZE);
	memcpy(p, ansipage, sizeof(unsigned char) * CODE_MAP_SIZE);
	codemap[0] = p;

	p = (unsigned char *)malloc(sizeof(unsigned char) * CODE_MAP_SIZE);
	memcpy(p, sym2, sizeof(unsigned char) * CODE_MAP_SIZE); //0x30
	codemap[0x30] = p;

	p = (unsigned char *)malloc(sizeof(unsigned char) * CODE_MAP_SIZE);
	memcpy(p, sym1, sizeof(unsigned char) * CODE_MAP_SIZE); //0xff
	codemap[0xFF] = p;

	//make cjk-map
	//unicode Chinese number contains Complex Font Number
	//add 0x25cb?
	unsigned short chs_number[] ={
		0x4e00/*一*/, 0x4e8c/*二*/, 0x4e09/*三*/, 0x56db/*四*/, 0x4e94/*五*/, 0x516d/*六*/, 0x4e03/*七*/, 0x516b/*八*/, 0x4e5d/*九*/, 0x5341/*十*/,
		0x58f9/*壹*/, 0x8d30/*贰*/, 0x53c1/*叁*/, 0x8086/*肆*/, 0x4f0d/*伍*/, 0x9646/*陆*/, 0x67d2/*柒*/, 0x7396/*玖*/, 0x634c/*捌*/, 0x62fe/*拾*/,
		0x96f6/*零*/, 0x767e/*百*/, 0x5343/*千*/, 0x4e07/*万*/, 0x4ebf/*亿*/, 0x5146/*兆*/, 0x4e24/*两*/, 0x534a/*半*/,
		0x25cb/*00*/, 0x4f70/*佰*/, 0x4edf/*仟*/, 0x51e0/*几*/, 0x591a/*多*/, 0x6bcf/*每*/, 0x7b2c/*第*/, 0x6570/*数*/
	};
	int i = 0;
	unsigned char idx = 0;
	for(i=0; i< sizeof(chs_number)/sizeof(unsigned short); i++){
		unsigned char idx = chs_number[i]>>8; //high-byte
		p = codemap[idx];
		if(!p) {
			p = (unsigned char *)malloc(sizeof(unsigned char) * CODE_MAP_SIZE);
			memset(p, 0, sizeof(unsigned char) * CODE_MAP_SIZE);
			codemap[idx] = p;
		}
		idx = (unsigned char)chs_number[i]; //low-byte
		p[idx] = _n_; //change tag from m -> n used by pure Chinese number
	}

	//unicode Chinese Symb 
	//# NOTE 0x22xx, 0x23xx is number symbol,
	p = (unsigned char *)malloc(sizeof(unsigned char) * CODE_MAP_SIZE);
	memset(p, _s_, sizeof(unsigned char) * CODE_MAP_SIZE);
	codemap[0x22] = p;
	codemap[0x23] = p;

	//0x2018-0x201f && 0x2025-0x2027 && 0x2032-0x2037, //Not in Chinese Char range, used as sep. Generation Sep.
	p = (unsigned char *)malloc(sizeof(unsigned char) * CODE_MAP_SIZE);
	memset(p, 0, sizeof(unsigned char) * CODE_MAP_SIZE);
	for(idx = (unsigned char)0x2018; idx <= (unsigned char)0x201f; idx++)
		p[idx] = _d_;
	for(idx = (unsigned char)0x2025; idx <= (unsigned char)0x2027; idx++)
		p[idx] = _d_;
	for(idx = (unsigned char)0x2032; idx<= (unsigned char)0x2037; idx++)
		p[idx] = _d_;
	codemap[0x20] = p;
	binit = 1;
	return 0;
}

unsigned char tag(unsigned short code)
{
	if(code == 0){
		return _u_;
	}
	unsigned char idx = code>>8;
	if(codemap[idx] && (idx=codemap[idx][(unsigned char)code]) != _u_)
		return idx;
	if ((code >= 0x4E00)&&(code <= 0x9FA5)) //Chinese Char
		return _z_;
	return _u_;
}

int utf8decode(const char *p, unsigned short *codep)
{
	if(p == NULL || binit == 0){
		return -1;
	}

	unsigned char c = *p;
	if ( c == 0 ){
		*codep = 0;
		return 0;
	}
	p++;

	// check for 7-bit case
	if ( c<128 ){
		*codep = c;
		return 1;
	}

	// get number of bytes
	int len = 0;
	while ( c & 0x80 ){
		len++;
		c <<= 1;
	}

	// check for valid number of bytes
	if ( len<2 || len>4 ){
		return -2;
	}	
	int i = len - 1;
	*codep = ( c>>len );
	do{
		if ( !(*p) )
			return 0; // unexpected eof

		if ( ((*p) & 0xC0)!=0x80 ) //10xxxxxx
			return -3; // invalid code

		*codep = ( (*codep) << 6 ) + ( (*p) & 0x3F ); //( code<<6 ) + ( 10xxxxxx & 111111 )
		i--;
		p++;
	} while ( i );

	return len;
}

int free_codemap()
{
	if(binit == 0){
		return 1;
	}
	int i = 0;
	for(i=0; i<CODE_MAP_SIZE; i++){
		if(codemap[i] && i != 0x23){
			free(codemap[i]);
			codemap[i] = NULL;
		}
	}
	binit = 1;
	return 0;
}

#ifdef CHARSET_MAIN
int main(int argc, char ** argv)
{
	char str[1024] = {0};
	if(argc == 1){
		strcpy(str, "测试! aaron@feinno	五个，。ｐｐ中1\r\n");
	}else{
		strcpy(str, argv[1]);
	}
	init_codemap();
	int off = 0;
	int ret = 0;
	unsigned char t = 0;
	unsigned short code = 0;
	char out[4];
	while(off < strlen(str)){
		ret = utf8decode(str+off, &code);
		if(ret < 0){
			printf("err: %d\n", ret);
			return 1;
		}
		t = tag(code);
		strncpy(out, str+off, ret);
		out[ret] = 0;
		printf("0x%02x[0x%04x]-%d\t%s\n", t, code, ret, out);
		off += ret;
	}
	free_codemap();
	return 0;
}
#endif
